package com.hsogoo.avatar.transport.netty;

import java.net.SocketAddress;
import java.util.concurrent.ThreadFactory;

import com.hsogoo.avatar.constants.AvatarConstant;
import com.hsogoo.avatar.enums.Protocol;
import com.hsogoo.avatar.transport.AvatarAcceptor;
import com.hsogoo.avatar.transport.AvatarProcessor;
import com.hsogoo.avatar.transport.netty.handler.AvatarAcceptorHandler;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.concurrent.DefaultThreadFactory;
import lombok.extern.slf4j.Slf4j;

/**
 * @Author huangsaigang
 * Created @2018/9/29.
 */
@Slf4j
public class AvatarNettyAcceptor implements AvatarAcceptor {

    protected final Protocol protocol;
    protected final SocketAddress localAddress;

    private final int nBosses;
    private final int nWorkers;

    private ServerBootstrap bootstrap;
    private EventLoopGroup boss;
    private EventLoopGroup worker;

    private AvatarProcessor processor;

    public AvatarNettyAcceptor(Protocol protocol, SocketAddress localAddress) {
        this(protocol, localAddress, AvatarConstant.AVAILABLE_PROCESSORS << 1);
    }

    public AvatarNettyAcceptor(Protocol protocol, SocketAddress localAddress, int nWorkers) {
        this(protocol, localAddress, 1, nWorkers);
    }

    public AvatarNettyAcceptor(Protocol protocol, SocketAddress localAddress, int nBosses, int nWorkers) {
        this.protocol = protocol;
        this.localAddress = localAddress;
        this.nBosses = nBosses;
        this.nWorkers = nWorkers;
    }

    @Override
    public void start() throws InterruptedException {
        init();
        bootstrap.channelFactory(SocketChannelProvider.AVATAR_ACCEPTOR);
        bootstrap.childHandler(new ChannelInitializer<Channel>() {

            @Override
            protected void initChannel(Channel ch) throws Exception {
                ch.pipeline().addLast(new ProtocolDecoder());
                ch.pipeline().addLast(new AvatarAcceptorHandler(processor));
                ch.pipeline().addLast(new ProtocolEncoder());
            }
        });
        ChannelFuture future = bootstrap.bind(localAddress).sync();
//        future.channel().closeFuture().sync();
        System.out.println("netty 启动成功。。。");
    }

    @Override
    public void shutdownGracefully() {
        boss.shutdownGracefully().syncUninterruptibly();
        worker.shutdownGracefully().syncUninterruptibly();
    }

    private void init(){
        ThreadFactory bossFactory = bossThreadFactory("avatar.acceptor.boss");
        ThreadFactory workerFactory = workerThreadFactory("avatar.acceptor.worker");
        boss = initEventLoopGroup(nBosses, bossFactory);
        worker = initEventLoopGroup(nWorkers, workerFactory);
        bootstrap = new ServerBootstrap().group(boss, worker);
        setOptions();
    }

    private ThreadFactory bossThreadFactory(String name) {
        return new DefaultThreadFactory(name, Thread.MAX_PRIORITY);
    }

    private ThreadFactory workerThreadFactory(String name) {
        return new DefaultThreadFactory(name, Thread.MAX_PRIORITY);
    }

    private EventLoopGroup initEventLoopGroup(int nThreads, ThreadFactory tFactory) {
        return new NioEventLoopGroup(nThreads, tFactory);
    }

    /**
     * set more options
     */
    private void setOptions() {
        bootstrap.option(ChannelOption.SO_BACKLOG, 128);
        bootstrap.childOption(ChannelOption.SO_KEEPALIVE, false);
    }

    @Override
    public Protocol protocol() {
        return protocol;
    }

    @Override
    public SocketAddress localAddress() {
        return localAddress;
    }

    @Override
    public AvatarProcessor processor() {
        return processor;
    }

    @Override
    public void withProcessor(AvatarProcessor processor) {
        this.processor = processor;
    }

}
